﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using IMCOM.Imagiality.Core;
using IMCOM.Imagiality.ImageProcessing.Contrast;
using OpenCvSharp;
namespace IMCOM.Imagiality.ImageProcessing.Canvas2D.AutoContrast
{

    public interface Canvas2DAutoContrastCalculatorObserver
    {
        void Notify(Canvas2DAutoContrastCalculator calculator);
    }

    public class Canvas2DAutoContrastCalculator : AutoContrastCalculator
    {
        private Point startPoint;
        private Point endPoint;
        private IList<Canvas2DAutoContrastCalculatorObserver> observers;
        private int sampleCount;
        private double[] colorArray;
        private GenericImage image;
        private Canvas2DGUI gui;
        private ContrastMeasurerGUI measurerGUI;

        public Canvas2DAutoContrastCalculator()
        {
            observers = new List<Canvas2DAutoContrastCalculatorObserver>();
        }

        public Canvas2DAutoContrastCalculator(Canvas2DGUI gui, ContrastMeasurerGUI measurerGUI) : this()
        {
            this.gui = gui;
            this.measurerGUI = measurerGUI;
        }

        public int SampleCount
        {
            get
            {
                return sampleCount;
            }
            set
            {
                sampleCount = value;
            }
        }

        public Point LineStartPoint
        {
            set
            {
                startPoint = value;
                notifyObservers();
            }
            get
            {
                return startPoint;
            }
        }

        public Point LineEndPoint
        {
            set
            {
                endPoint = value;
                notifyObservers();
            }
            get
            {
                return endPoint;
            }
        }


        public AutoContrastValue Calculate(GenericImage image)
        {
            this.image = image;

            if (gui != null && measurerGUI != null)
            {
                // Monitor.Wait
            }
            else 
            {
                // dummy sample count
                sampleCount = 1000;

                gui = new Canvas2DGUI(new CanvasImageWrapper(image));
                gui.ImageEventListener = new AutoContrastImageEventListener(gui, this);
                
                measurerGUI = new ContrastMeasurerGUI();
                AddObserver(measurerGUI);

                Panel canvas2DPanel = new Panel();
                canvas2DPanel.Controls.Add(gui);
                canvas2DPanel.Dock = DockStyle.Fill;

                Panel measurerPanel = new Panel();
                measurerPanel.Controls.Add(measurerGUI);
                measurerPanel.Dock = DockStyle.Bottom;

                Form f1 = new Form();
                f1.Controls.Add(canvas2DPanel);
                f1.Controls.Add(measurerPanel);
                Application.Run(f1);
            }

            
            AutoContrastValue val = new AutoContrastValue(GetColorArray(), GetDistanceArray(), sampleCount);
            return val;
        }

        public double[] GetColorArray()
        {
            if (image == null || SampleCount == 0)
            {
                colorArray = new double[0];
                return colorArray;
            }

            colorArray = new double[SampleCount];

            Point[] samplePoints = new Point[SampleCount];
            samplePoints[0] = startPoint;
            samplePoints[SampleCount - 1] = endPoint;

            int horizontalDistance = endPoint.X - startPoint.X;
            int verticalDistance = endPoint.Y - startPoint.Y;
            for (int i = 1; i < SampleCount - 1; i++)
            {
                int newSamplePointX = startPoint.X + (int)((double)horizontalDistance / (double)(SampleCount - 1) * i);
                int newSamplePointY = startPoint.Y + (int)((double)verticalDistance / (double)(SampleCount - 1) * i);

                samplePoints[i] = new Point(newSamplePointX, newSamplePointY);
            }

            IplImage iplImage = image.getIplImage();

            CvColor tempCvColor;
            for (int i = 0; i < samplePoints.Length; i++)
            {
                if (samplePoints[i].X >= 0 && samplePoints[i].X < iplImage.Width && samplePoints[i].Y >= 0 && samplePoints[i].Y < iplImage.Height)
                {
                    tempCvColor = iplImage[samplePoints[i].Y, samplePoints[i].X];
                    //colorArray[i] = tempCvColor.R * 0.3 + tempCvColor.G * 0.59 + tempCvColor.B * 0.11;
                    colorArray[i] = (tempCvColor.R + tempCvColor.G + tempCvColor.B) / 3;
                }
            }

            return colorArray;
        }

        public double[] GetDistanceArray()
        {

            if (image == null || SampleCount == 0) return new double[0];

            double totalDistance = Math.Sqrt(Math.Pow((endPoint.X - startPoint.X), 2) + Math.Pow((endPoint.Y - startPoint.Y), 2));
            double distanceBetween2Samples = totalDistance / (SampleCount - 1);

            double[] distanceArray = new double[SampleCount];

            for (int i = 0; i < sampleCount; i++)
            {
                distanceArray[i] = (double)i * distanceBetween2Samples;
            }

            return distanceArray;
        }

        public void SetLineStartAndEndPoints(Point startPoint, Point endPoint)
        {
            this.startPoint = startPoint;
            this.endPoint = endPoint;
            notifyObservers();
        }

        public void AddObserver(Canvas2DAutoContrastCalculatorObserver observer)
        {
            observers.Add(observer);
        }

        private void notifyObservers()
        {
            foreach (Canvas2DAutoContrastCalculatorObserver observer in observers)
                observer.Notify(this);
        }

    }
}
